home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / stk-3.002 / stk-3 / STk-3.1 / Tk / generic / tkClipboard.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-31  |  17.4 KB  |  607 lines

  1. /*
  2.  * tkClipboard.c --
  3.  *
  4.  *     This file manages the clipboard for the Tk toolkit,
  5.  *     maintaining a collection of data buffers that will be
  6.  *     supplied on demand to requesting applications.
  7.  *
  8.  * Copyright (c) 1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkClipboard.c 1.14 96/02/15 18:52:37
  15.  */
  16.  
  17. #include "tkInt.h"
  18. #include "tkPort.h"
  19. #include "tkSelect.h"
  20.  
  21. /*
  22.  * Prototypes for procedures used only in this file:
  23.  */
  24.  
  25. static int        ClipboardAppHandler _ANSI_ARGS_((ClientData clientData,
  26.                 int offset, char *buffer, int maxBytes));
  27. static int        ClipboardHandler _ANSI_ARGS_((ClientData clientData,
  28.                 int offset, char *buffer, int maxBytes));
  29. static int        ClipboardWindowHandler _ANSI_ARGS_((
  30.                 ClientData clientData, int offset, char *buffer,
  31.                 int maxBytes));
  32. static void        ClipboardLostSel _ANSI_ARGS_((ClientData clientData));
  33.  
  34. /*
  35.  *----------------------------------------------------------------------
  36.  *
  37.  * ClipboardHandler --
  38.  *
  39.  *    This procedure acts as selection handler for the
  40.  *    clipboard manager.  It extracts the required chunk of
  41.  *    data from the buffer chain for a given selection target.
  42.  *
  43.  * Results:
  44.  *    The return value is a count of the number of bytes
  45.  *    actually stored at buffer.
  46.  *
  47.  * Side effects:
  48.  *    None.
  49.  *
  50.  *----------------------------------------------------------------------
  51.  */
  52.  
  53. static int
  54. ClipboardHandler(clientData, offset, buffer, maxBytes)
  55.     ClientData clientData;    /* Information about data to fetch. */
  56.     int offset;            /* Return selection bytes starting at this
  57.                  * offset. */
  58.     char *buffer;        /* Place to store converted selection. */
  59.     int maxBytes;        /* Maximum # of bytes to store at buffer. */
  60. {
  61.     TkClipboardTarget *targetPtr = (TkClipboardTarget*) clientData;
  62.     TkClipboardBuffer *cbPtr;
  63.     char *srcPtr, *destPtr;
  64.     int count = 0;
  65.     int scanned = 0;
  66.     size_t length, freeCount;
  67.  
  68.     /*
  69.      * Skip to buffer containing offset byte
  70.      */
  71.  
  72.     for (cbPtr = targetPtr->firstBufferPtr; ; cbPtr = cbPtr->nextPtr) {
  73.     if (cbPtr == NULL) {
  74.         return 0;
  75.     }
  76.     if (scanned + cbPtr->length > offset) {
  77.         break;
  78.     }
  79.     scanned += cbPtr->length;
  80.     }
  81.  
  82.     /*
  83.      * Copy up to maxBytes or end of list, switching buffers as needed.
  84.      */
  85.  
  86.     freeCount = maxBytes;
  87.     srcPtr = cbPtr->buffer + (offset - scanned);
  88.     destPtr = buffer;
  89.     length = cbPtr->length - (offset - scanned);
  90.     while (1) {
  91.     if (length > freeCount) {
  92.         strncpy(destPtr, srcPtr, freeCount);
  93.         return maxBytes;
  94.     } else {
  95.         strncpy(destPtr, srcPtr, length);
  96.         destPtr += length;
  97.         count += length;
  98.         freeCount -= length;
  99.     }
  100.     cbPtr = cbPtr->nextPtr;
  101.     if (cbPtr == NULL) {
  102.         break;
  103.     }
  104.     srcPtr = cbPtr->buffer;
  105.     length = cbPtr->length;
  106.     }
  107.     return count;
  108. }
  109.  
  110. /*
  111.  *----------------------------------------------------------------------
  112.  *
  113.  * ClipboardAppHandler --
  114.  *
  115.  *    This procedure acts as selection handler for retrievals of type
  116.  *    TK_APPLICATION.  It returns the name of the application that
  117.  *    owns the clipboard.  Note:  we can't use the default Tk
  118.  *    selection handler for this selection type, because the clipboard
  119.  *    window isn't a "real" window and doesn't have the necessary
  120.  *    information.
  121.  *
  122.  * Results:
  123.  *    The return value is a count of the number of bytes
  124.  *    actually stored at buffer.
  125.  *
  126.  * Side effects:
  127.  *    None.
  128.  *
  129.  *----------------------------------------------------------------------
  130.  */
  131.  
  132. static int
  133. ClipboardAppHandler(clientData, offset, buffer, maxBytes)
  134.     ClientData clientData;    /* Pointer to TkDisplay structure. */
  135.     int offset;            /* Return selection bytes starting at this
  136.                  * offset. */
  137.     char *buffer;        /* Place to store converted selection. */
  138.     int maxBytes;        /* Maximum # of bytes to store at buffer. */
  139. {
  140.     TkDisplay *dispPtr = (TkDisplay *) clientData;
  141.     size_t length;
  142.     char *p;
  143.  
  144.     p = dispPtr->clipboardAppPtr->winPtr->nameUid;
  145.     length = strlen(p);
  146.     length -= offset;
  147.     if (length <= 0) {
  148.     return 0;
  149.     }
  150.     if (length > maxBytes) {
  151.     length = maxBytes;
  152.     }
  153.     strncpy(buffer, p, length);
  154.     return length;
  155. }
  156.  
  157. /*
  158.  *----------------------------------------------------------------------
  159.  *
  160.  * ClipboardWindowHandler --
  161.  *
  162.  *    This procedure acts as selection handler for retrievals of
  163.  *    type TK_WINDOW.  Since the clipboard doesn't correspond to
  164.  *    any particular window, we just return ".".  We can't use Tk's
  165.  *    default handler for this selection type, because the clipboard
  166.  *    window isn't a valid window.
  167.  *
  168.  * Results:
  169.  *    The return value is 1, the number of non-null bytes stored
  170.  *    at buffer.
  171.  *
  172.  * Side effects:
  173.  *    None.
  174.  *
  175.  *----------------------------------------------------------------------
  176.  */
  177.  
  178. static int
  179. ClipboardWindowHandler(clientData, offset, buffer, maxBytes)
  180.     ClientData clientData;    /* Not used. */
  181.     int offset;            /* Return selection bytes starting at this
  182.                  * offset. */
  183.     char *buffer;        /* Place to store converted selection. */
  184.     int maxBytes;        /* Maximum # of bytes to store at buffer. */
  185. {
  186.     buffer[0] = '.';
  187.     buffer[1] = 0;
  188.     return 1;
  189. }
  190.  
  191. /*
  192.  *----------------------------------------------------------------------
  193.  *
  194.  * ClipboardLostSel --
  195.  *
  196.  *    This procedure is invoked whenever clipboard ownership is
  197.  *    claimed by another window.  It just sets a flag so that we
  198.  *    know the clipboard was taken away.
  199.  *
  200.  * Results:
  201.  *    None.
  202.  *
  203.  * Side effects:
  204.  *    The clipboard is marked as inactive.
  205.  *
  206.  *----------------------------------------------------------------------
  207.  */
  208.  
  209. static void
  210. ClipboardLostSel(clientData)
  211.     ClientData clientData;        /* Pointer to TkDisplay structure. */
  212. {
  213.     TkDisplay *dispPtr = (TkDisplay*) clientData;
  214.  
  215.     dispPtr->clipboardActive = 0;
  216. }
  217.  
  218. /*
  219.  *----------------------------------------------------------------------
  220.  *
  221.  * Tk_ClipboardClear --
  222.  *
  223.  *    Take control of the clipboard and clear out the previous
  224.  *    contents.  This procedure must be invoked before any
  225.  *    calls to Tk_AppendToClipboard.
  226.  *
  227.  * Results:
  228.  *    A standard Tcl result.  If an error occurs, an error message is
  229.  *    left in interp->result.
  230.  *
  231.  * Side effects:
  232.  *    From now on, requests for the CLIPBOARD selection will be
  233.  *    directed to the clipboard manager routines associated with
  234.  *    clipWindow for the display of tkwin.  In order to guarantee
  235.  *    atomicity, no event handling should occur between
  236.  *    Tk_ClipboardClear and the following Tk_AppendToClipboard
  237.  *    calls.  This procedure may cause a user-defined LostSel command 
  238.  *     to be invoked when the CLIPBOARD is claimed, so any calling
  239.  *    function should be reentrant at the point Tk_ClipboardClear is
  240.  *    invoked.
  241.  *
  242.  *----------------------------------------------------------------------
  243.  */
  244.  
  245. int
  246. Tk_ClipboardClear(interp, tkwin)
  247.     Tcl_Interp *interp;        /* Interpreter to use for error reporting. */
  248.     Tk_Window tkwin;        /* Window in application that is clearing
  249.                  * clipboard;  identifies application and
  250.                  * display. */
  251. {
  252.     TkWindow *winPtr = (TkWindow *) tkwin;
  253.     TkDisplay *dispPtr = winPtr->dispPtr;
  254.     TkClipboardTarget *targetPtr, *nextTargetPtr;
  255.     TkClipboardBuffer *cbPtr, *nextCbPtr;
  256.  
  257.     if (dispPtr->clipWindow == NULL) {
  258.     int result;
  259.  
  260.     result = TkClipInit(interp, dispPtr);
  261.     if (result != TCL_OK) {
  262.         return result;
  263.     }
  264.     }
  265.  
  266.     /*
  267.      * Discard any existing clipboard data and delete the selection
  268.      * handler(s) associated with that data.
  269.      */
  270.  
  271.     for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL;
  272.         targetPtr = nextTargetPtr) {
  273.     for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL;
  274.         cbPtr = nextCbPtr) {
  275.         ckfree(cbPtr->buffer);
  276.         nextCbPtr = cbPtr->nextPtr;
  277.         ckfree((char *) cbPtr);
  278.     }
  279.     nextTargetPtr = targetPtr->nextPtr;
  280.     Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  281.         targetPtr->type);
  282.     ckfree((char *) targetPtr);
  283.     }
  284.     dispPtr->clipTargetPtr = NULL;
  285.  
  286.     /*
  287.      * Reclaim the clipboard selection if we lost it.
  288.      */
  289.  
  290.     if (!dispPtr->clipboardActive) {
  291.     Tk_OwnSelection(dispPtr->clipWindow, dispPtr->clipboardAtom,
  292.         ClipboardLostSel, (ClientData) dispPtr);
  293.     dispPtr->clipboardActive = 1;
  294.     }
  295.     dispPtr->clipboardAppPtr = winPtr->mainPtr;
  296.     return TCL_OK;
  297. }
  298.  
  299. /*
  300.  *----------------------------------------------------------------------
  301.  *
  302.  * Tk_ClipboardAppend --
  303.  *
  304.  *     Append a buffer of data to the clipboard.  The first buffer of
  305.  *    a given type determines the format for that type.  Any successive
  306.  *    appends to that type must have the same format or an error will
  307.  *    be returned.  Tk_ClipboardClear must be called before a sequence
  308.  *    of Tk_ClipboardAppend calls can be issued.  In order to guarantee
  309.  *    atomicity, no event handling should occur between Tk_ClipboardClear
  310.  *    and the following Tk_AppendToClipboard calls.
  311.  *
  312.  * Results:
  313.  *    A standard Tcl result.  If an error is returned, an error message
  314.  *    is left in interp->result.
  315.  *
  316.  * Side effects:
  317.  *     The specified buffer will be copied onto the end of the clipboard.
  318.  *    The clipboard maintains a list of buffers which will be used to
  319.  *    supply the data for a selection get request.  The first time a given
  320.  *    type is appended, Tk_ClipboardAppend will register a selection
  321.  *     handler of the appropriate type.
  322.  *
  323.  *----------------------------------------------------------------------
  324.  */
  325.  
  326. int
  327. Tk_ClipboardAppend(interp, tkwin, type, format, buffer)
  328.     Tcl_Interp *interp;        /* Used for error reporting. */
  329.     Tk_Window tkwin;        /* Window that selects a display. */
  330.     Atom type;            /* The desired conversion type for this
  331.                  * clipboard item, e.g. STRING or LENGTH. */
  332.     Atom format;        /* Format in which the selection
  333.                  * information should be returned to
  334.                  * the requestor. */
  335.     char* buffer;        /* NULL terminated string containing the data
  336.                  * to be added to the clipboard. */
  337. {
  338.     TkWindow *winPtr = (TkWindow *) tkwin;
  339.     TkDisplay *dispPtr = winPtr->dispPtr;
  340.     TkClipboardTarget *targetPtr;
  341.     TkClipboardBuffer *cbPtr;
  342.  
  343.     /*
  344.      * If this application doesn't already own the clipboard, clear
  345.      * the clipboard.  If we don't own the clipboard selection, claim it.
  346.      */
  347.  
  348.     if (dispPtr->clipboardAppPtr != winPtr->mainPtr) {
  349.     Tk_ClipboardClear(interp, tkwin);
  350.     } else if (!dispPtr->clipboardActive) {
  351.     Tk_OwnSelection(dispPtr->clipWindow, dispPtr->clipboardAtom,
  352.         ClipboardLostSel, (ClientData) dispPtr);
  353.     dispPtr->clipboardActive = 1;
  354.     }
  355.  
  356.     /*
  357.      * Check to see if the specified target is already present on the
  358.      * clipboard.  If it isn't, we need to create a new target; otherwise,
  359.      * we just append the new buffer to the clipboard list.
  360.      */
  361.  
  362.     for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL;
  363.         targetPtr = targetPtr->nextPtr) {
  364.     if (targetPtr->type == type)
  365.         break;
  366.     }
  367.     if (targetPtr == NULL) {
  368.     targetPtr = (TkClipboardTarget*) ckalloc(sizeof(TkClipboardTarget));
  369.     targetPtr->type = type;
  370.     targetPtr->format = format;
  371.     targetPtr->firstBufferPtr = targetPtr->lastBufferPtr = NULL;
  372.     targetPtr->nextPtr = dispPtr->clipTargetPtr;
  373.     dispPtr->clipTargetPtr = targetPtr;
  374.     Tk_CreateSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  375.         type, ClipboardHandler, (ClientData) targetPtr, format);
  376.     } else if (targetPtr->format != format) {
  377.     Tcl_AppendResult(interp, "format \"", Tk_GetAtomName(tkwin, format),
  378.         "\" does not match current format \"",
  379.         Tk_GetAtomName(tkwin, targetPtr->format),"\" for ",
  380.         Tk_GetAtomName(tkwin, type), (char *) NULL);
  381.     return TCL_ERROR;
  382.     }
  383.  
  384.     /*
  385.      * Append a new buffer to the buffer chain.
  386.      */
  387.  
  388.     cbPtr = (TkClipboardBuffer*) ckalloc(sizeof(TkClipboardBuffer));
  389.     cbPtr->nextPtr = NULL;
  390.     if (targetPtr->lastBufferPtr != NULL) {
  391.     targetPtr->lastBufferPtr->nextPtr = cbPtr;
  392.     } else {
  393.     targetPtr->firstBufferPtr = cbPtr;
  394.     }
  395.     targetPtr->lastBufferPtr = cbPtr;
  396.  
  397.     cbPtr->length = strlen(buffer);
  398.     cbPtr->buffer = (char *) ckalloc((unsigned) (cbPtr->length + 1));
  399.     strcpy(cbPtr->buffer, buffer);
  400.  
  401.     TkSelUpdateClipboard((TkWindow*)(dispPtr->clipWindow), targetPtr);
  402.  
  403.     return TCL_OK;
  404. }
  405.  
  406. /*
  407.  *----------------------------------------------------------------------
  408.  *
  409.  * Tk_ClipboardCmd --
  410.  *
  411.  *    This procedure is invoked to process the "clipboard" Tcl
  412.  *    command.  See the user documentation for details on what
  413.  *    it does.
  414.  *
  415.  * Results:
  416.  *    A standard Tcl result.
  417.  *
  418.  * Side effects:
  419.  *    See the user documentation.
  420.  *
  421.  *----------------------------------------------------------------------
  422.  */
  423.  
  424. int
  425. Tk_ClipboardCmd(clientData, interp, argc, argv)
  426.     ClientData clientData;    /* Main window associated with
  427.                  * interpreter. */
  428.     Tcl_Interp *interp;        /* Current interpreter. */
  429.     int argc;            /* Number of arguments. */
  430.     char **argv;        /* Argument strings. */
  431. {
  432.     Tk_Window tkwin = (Tk_Window) clientData;
  433.     char *path = NULL;
  434.     size_t length;
  435.     int count;
  436.     char c;
  437.     char **args;
  438.  
  439.     if (argc < 2) {
  440.     Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  441.         " option ?arg arg ...?\"", (char *) NULL);
  442.     return TCL_ERROR;
  443.     }
  444.     c = argv[1][0];
  445.     length = strlen(argv[1]);
  446.     if ((c == 'a') && (strncmp(argv[1], "append", length) == 0)) {
  447.     Atom target, format;
  448.     char *targetName = NULL;
  449.     char *formatName = NULL;
  450.  
  451.     for (count = argc-2, args = argv+2; count > 1; count -= 2, args += 2) {
  452.         if (args[0][0] != '-') {
  453.         break;
  454.         }
  455.         c = args[0][1];
  456.         length = strlen(args[0]);
  457.         if ((c == '-') && (length == 2)) {
  458.         args++;
  459.         count--;
  460.         break;
  461.         }
  462.         if ((c == 'd') && (strncmp(args[0], "-displayof", length) == 0)) {
  463.         path = args[1];
  464.         } else if ((c == 'f')
  465.             && (strncmp(args[0], "-format", length) == 0)) {
  466.         formatName = args[1];
  467.         } else if ((c == 't')
  468.             && (strncmp(args[0], "-type", length) == 0)) {
  469.         targetName = args[1];
  470.         } else {
  471.         Tcl_AppendResult(interp, "unknown option \"", args[0],
  472.             "\"", (char *) NULL);
  473.         return TCL_ERROR;
  474.         }
  475.     }
  476.     if (count != 1) {
  477.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  478.             " append ?options? data\"", (char *) NULL);
  479.         return TCL_ERROR;
  480.     }
  481.     if (path != NULL) {
  482.         tkwin = Tk_NameToWindow(interp, path, tkwin);
  483.     }
  484.     if (tkwin == NULL) {
  485.         return TCL_ERROR;
  486.     }
  487.     if (targetName != NULL) {
  488.         target = Tk_InternAtom(tkwin, targetName);
  489.     } else {
  490.         target = XA_STRING;
  491.     }
  492.     if (formatName != NULL) {
  493.         format = Tk_InternAtom(tkwin, formatName);
  494.     } else {
  495.         format = XA_STRING;
  496.     }
  497.     return Tk_ClipboardAppend(interp, tkwin, target, format, args[0]);
  498.     } else if ((c == 'c') && (strncmp(argv[1], "clear", length) == 0)) {
  499.     for (count = argc-2, args = argv+2; count > 0; count -= 2, args += 2) {
  500.         if (args[0][0] != '-') {
  501.         break;
  502.         }
  503.         if (count < 2) {
  504.         Tcl_AppendResult(interp, "value for \"", *args,
  505.             "\" missing", (char *) NULL);
  506.         return TCL_ERROR;
  507.         }
  508.         c = args[0][1];
  509.         length = strlen(args[0]);
  510.         if ((c == 'd') && (strncmp(args[0], "-displayof", length) == 0)) {
  511.         path = args[1];
  512.         } else {
  513.         Tcl_AppendResult(interp, "unknown option \"", args[0],
  514.             "\"", (char *) NULL);
  515.         return TCL_ERROR;
  516.         }
  517.     }
  518.     if (count > 0) {
  519.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  520.             " clear ?options?\"", (char *) NULL);
  521.         return TCL_ERROR;
  522.     }
  523.     if (path != NULL) {
  524.         tkwin = Tk_NameToWindow(interp, path, tkwin);
  525.     }
  526.     if (tkwin == NULL) {
  527.         return TCL_ERROR;
  528.     }
  529.     return Tk_ClipboardClear(interp, tkwin);
  530.     } else {
  531.     sprintf(interp->result,
  532.         "bad option \"%.50s\": must be clear or append",
  533.         argv[1]);
  534.     return TCL_ERROR;
  535.     }
  536. }
  537.  
  538. /*
  539.  *----------------------------------------------------------------------
  540.  *
  541.  * TkClipInit --
  542.  *
  543.  *    This procedure is called to initialize the window for claiming
  544.  *    clipboard ownership and for receiving selection get results.  This
  545.  *    function is called from tkSelect.c as well as tkClipboard.c.
  546.  *
  547.  * Results:
  548.  *    The result is a standard Tcl return value, which is normally TCL_OK.
  549.  *    If an error occurs then an error message is left in interp->result
  550.  *    and TCL_ERROR is returned.
  551.  *
  552.  * Side effects:
  553.  *    Sets up the clipWindow and related data structures.
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557.  
  558. int
  559. TkClipInit(interp, dispPtr)
  560.     Tcl_Interp *interp;        /* Interpreter to use for error
  561.                  * reporting. */
  562.     register TkDisplay *dispPtr;/* Display to initialize. */
  563. {
  564.     XSetWindowAttributes atts;
  565.  
  566.     dispPtr->clipTargetPtr = NULL;
  567.     dispPtr->clipboardActive = 0;
  568.     dispPtr->clipboardAppPtr = NULL;
  569.     
  570.     /*
  571.      * Create the window used for clipboard ownership and selection retrieval,
  572.      * and set up an event handler for it.
  573.      */
  574.  
  575.     dispPtr->clipWindow = Tk_CreateWindow(interp, (Tk_Window) NULL,
  576.         "_clip", DisplayString(dispPtr->display));
  577.     if (dispPtr->clipWindow == NULL) {
  578.     return TCL_ERROR;
  579.     }
  580.     atts.override_redirect = True;
  581.     Tk_ChangeWindowAttributes(dispPtr->clipWindow, CWOverrideRedirect, &atts);
  582.     Tk_MakeWindowExist(dispPtr->clipWindow);
  583.  
  584.     if (dispPtr->multipleAtom == None) {
  585.     /*
  586.      * Need to invoke selection initialization to make sure that
  587.      * atoms we depend on below are defined.
  588.      */
  589.  
  590.     TkSelInit(dispPtr->clipWindow);
  591.     }
  592.  
  593.     /*
  594.      * Create selection handlers for types TK_APPLICATION and TK_WINDOW
  595.      * on this window.  Can't use the default handlers for these types
  596.      * because this isn't a full-fledged window.
  597.      */
  598.  
  599.     Tk_CreateSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  600.         dispPtr->applicationAtom, ClipboardAppHandler,
  601.         (ClientData) dispPtr, XA_STRING);
  602.     Tk_CreateSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom,
  603.         dispPtr->windowAtom, ClipboardWindowHandler,
  604.         (ClientData) dispPtr, XA_STRING);
  605.     return TCL_OK;
  606. }
  607.